package ch.ethz.fcl.metrobuzz.controller;

import org.lwjgl.input.Mouse;

import ch.ethz.fcl.metrobuzz.MBContainer;
import ch.ethz.fcl.metrobuzz.MBProperty;
import ch.ethz.fcl.metrobuzz.controller.external.ActionEvent;
import ch.ethz.fcl.metrobuzz.controller.external.key.KeyListener;
import ch.ethz.fcl.metrobuzz.controller.external.mouse.MouseListener;
import ch.ethz.fcl.metrobuzz.controller.external.tuio.MBTuioEvent;
import ch.ethz.fcl.metrobuzz.controller.external.tuio.MBTuioListener;
import ch.ethz.fcl.metrobuzz.controller.gui.twl.MBTWLGUI;
import ch.ethz.fcl.metrobuzz.controller.timer.Timer;
import ch.ethz.fcl.metrobuzz.render.techniques.projection.Projection;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;
import ch.ethz.fcl.metrobuzz.spatialindex.object3d.Point3D;
import ch.ethz.fcl.metrobuzz.viewer.MBViewer;

public class MBController {
	private MBContainer container;
	private MBViewer view;

	private int eventInterval = 20;

	private KeyListener key = null;
	private MouseListener mouse = null;
	private MBTWLGUI twlGUI = null;
	private MBTuioListener tuio = null;

	private boolean reset = false;

	public boolean updateMouseEvent = true;

	public MBController(MBContainer container) {
		this.container = container;
		view = container.getViewer();

		container.setController(this);

		init();
	}

	public MBController(MBViewer view) {
		this.view = view;

		init();
	}

	private void init() {
		key = new KeyListener(this);
		mouse = new MouseListener(this);

		if (MBProperty.CONTROL_TIMEER) {
			Timer timer = new Timer(container);
			new Thread(timer).start();
		}

		if (MBProperty.CONTROL_TWL)
			twlGUI = new MBTWLGUI(this);

		tuio = new MBTuioListener(this);
	}

	public void update() {
		if (reset) {
			if (twlGUI != null) {
				twlGUI.getGUI().destroy();
				twlGUI.getRenderer().getActiveCacheContext().destroy();
				container.getViewer().recreate(false);
				twlGUI = new MBTWLGUI(this);
			}

			container.createFont();
			reset = false;
		}

		if (twlGUI != null)
			twlGUI.update();

		key.update();

		mouse.update();
		tuio.update();

		if (container.getRender() != null
				&& container.getRender().getInterchangeRender() != null
				&& container.getRender().getInterchangeRender()
						.getTemporalRender() != null)
			container.getRender().getInterchangeRender().getTemporalRender()
					.handleActionEvent();

		if (container.getRender() != null
				&& container.getRender().getODRender() != null
				&& container.getRender().getODRender().getMapView() != null)
			container.getRender().getODRender().getMapView()
					.handleActionEvent();

		if (container.getRender() != null
				&& container.getRender().getAccessibilityRender() != null)
			container.getRender().getAccessibilityRender().getMouseAction()
					.handleActionEvent();

		if (updateMouseEvent)
			handleActionEvent();
	}

	/**
	 * handle the drag event from both mouse and tuio
	 */
	private void handleActionEvent() {
		boolean drag = false;
		Projection proj = container.getViewer().getProjection();

		ActionEvent action = tuio.getTuioEvent();
		if (action != null)
			drag = (tuio.hasInput() && ActionEvent.DRAG_UPDATE);
		else {
			action = mouse.getLeftMouse();
			drag = ((action.getAction() != ActionEvent.ACTION_MOVE) && ActionEvent.DRAG_UPDATE);
		}

		if (drag) {
			Point3D mouseWorld = proj.getWorldCoord(
					new Point2D(action.getPreEventX(), action.getPreEventY()),
					0);
			Point3D newMouseWorld = proj.getWorldCoord(
					new Point2D(action.getEventX(), action.getEventY()), 0);
			float deltaX = newMouseWorld.getX() - mouseWorld.getX();
			float deltaY = newMouseWorld.getY() - mouseWorld.getY();

			if (action instanceof MBTuioEvent) {
				deltaX *= 2;
				deltaY *= 2;
			}

			container.getViewer().getCamera().addTrackX(deltaX);
			container.getViewer().getCamera().addTrackY(deltaY);

			MBViewer.newUpdate = true;
		}

		if (Mouse.isButtonDown(0)) {
			ActionEvent mouseEvent = mouse.getLeftMouse();

			float deltaX = mouseEvent.getEventX() - mouseEvent.getPreEventX();
			float deltaY = mouseEvent.getEventY() - mouseEvent.getPreEventY();

			container.getViewer().getCamera().addPanX(deltaY);
			container.getViewer().getCamera().addPanZ(deltaX);

			MBViewer.newUpdate = true;
		}
	}

	public void reset() {
		this.reset = true;
	}

	public MBViewer getViewer() {
		return view;
	}

	public MBContainer getContainer() {
		return container;
	}

	public MouseListener getMouseListener() {
		return mouse;
	}

	public MBTuioListener getTuioListener() {
		return tuio;
	}

	public void resetTWLGUI() {
		twlGUI = new MBTWLGUI(this);
	}

	public MBTWLGUI getTWLGUI() {
		return this.twlGUI;
	}

	public int getEventInterval() {
		return eventInterval;
	}

	public void setEventInterval(int interval) {
		this.eventInterval = interval;
	}
}
